package com.javaweb.web.controller;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import org.apache.http.entity.ContentType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.javaweb.annotation.url.ControllerMethod;
import com.javaweb.base.BaseConfigMemory;
import com.javaweb.base.BaseResponseResult;
import com.javaweb.base.BaseService;
import com.javaweb.base.BaseSystemMemory;
import com.javaweb.base.po.Module;
import com.javaweb.base.po.User;
import com.javaweb.config.hutool.HutoolCaptchaGenerator;
import com.javaweb.constant.CommonConstant;
import com.javaweb.constant.SystemConstant.AdminConstant;
import com.javaweb.constant.SystemConstant.LoginConstant;
import com.javaweb.enums.HttpCodeEnum;
import com.javaweb.exception.ServiceException;
import com.javaweb.util.core.DateUtil;
import com.javaweb.util.core.MathUtil;
import com.javaweb.util.core.ObjectOperateUtil;
import com.javaweb.util.core.PatternUtil;
import com.javaweb.util.core.RsaUtil;
import com.javaweb.util.core.SecretUtil;
import com.javaweb.util.core.StringUtil;
import com.javaweb.util.entity.RsaKey;
import com.javaweb.web.eo.TokenData;
import com.javaweb.web.eo.interfaces.ExcludeInfoResponse;
import com.javaweb.web.eo.module.SidebarInfoResponse;
import com.javaweb.web.eo.user.UserLoginRequest;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * 无需登录即可访问模块
 * <p>
 * 该模块主要提供的是用户登录接口和获取服务器时间接口及一些常见请求错误码返回的接口
 * @author tank2140896
 */
@RestController
public class AllOpenController extends BaseService {
	
	/**
	 * 获取服务器时间
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@GetMapping("/getServeTime")
	@ControllerMethod(interfaceName="获取服务器时间")
	public BaseResponseResult getServeTime() {
		return SUCCESS("serve.getServeTime.success",DateUtil.getStringDate(DateUtil.DEFAULT_DATETIME_PATTERN));
	}
	
	/* -------------------------------------------------- 分界线 -------------------------------------------------- */
	
	/**
	 * 请求接口受限
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/requestLimit",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="请求接口受限")
	public BaseResponseResult requestLimit() {
		return getBaseResponseResult(HttpCodeEnum.REQUEST_LIMIT,"validated.permission.requestLimit");
	}
	
	/**
	 * 请求参数缺失
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/requestParameterLost",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="请求参数缺失")
	public BaseResponseResult requestParameterLost() {
		return getBaseResponseResult(HttpCodeEnum.REQUEST_PARAMETER_LOST,"validated.permission.requestParameterLost");
	}
	
	/**
	 * 请求失效
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/invalidRequest",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="请求失效")
	public BaseResponseResult invalidRequest() {
		return getBaseResponseResult(HttpCodeEnum.INVALID_REQUEST,"validated.permission.invalidRequest");
	}
	
	/**
	 * 请求参数错误
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/requestParameterError",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="请求参数错误")
	public BaseResponseResult requestParameterError() {
		return getBaseResponseResult(HttpCodeEnum.REQUEST_PARAMETER_ERROR,"validated.permission.requestParameterError");
	}
	
	/**
	 * 没有权限
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/noAuthory",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="没有权限")
	public BaseResponseResult noAuthory() {
		return getBaseResponseResult(HttpCodeEnum.NO_AUTHORY,"validated.permission.noAuthory");
	}
	
	/**
	 * 请求接口不存在
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/notFound",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="请求接口不存在")
	public BaseResponseResult notFound() {
		return getBaseResponseResult(HttpCodeEnum.NOT_FOUND,"validated.permission.notFound");
	}
	
	/**
	 * 系统异常
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@RequestMapping(value="/internalError",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT,RequestMethod.DELETE})
	@ControllerMethod(interfaceName="系统异常")
	public BaseResponseResult internalError() {
		return getBaseResponseResult(HttpCodeEnum.INTERNAL_ERROR,"validated.permission.internalError");
	}
	
	/* -------------------------------------------------- 分界线 -------------------------------------------------- */
	
	/**
	 * 获取验证值
	 * <p>
	 * @param type 验证类型（1：二维码(实际应该是个URL，这里只做示例)；2：图形验证码；3：短信验证码）
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@GetMapping("/getVerifyValue/{type}")
	@ControllerMethod(interfaceName="获取验证值")
	public BaseResponseResult getVerifyValue(@PathVariable(value="type",required=true) String type) {
		var code = CommonConstant.EMPTY_VALUE;
		try {
			if(LoginConstant.VERIFY_PATTERN.matcher(type).matches()) {
				String time = DateUtil.getDefaultDate(DateUtil.DATETIME_PATTERN_TYPE1);
				code = switch(type) {
					case "1" -> SecretUtil.getRandomUUID(false);
					case "2" -> SecretUtil.defaultGenRandomPass(BaseConfigMemory.captchaRange,BaseConfigMemory.captchaSize);
					case "3" -> SecretUtil.defaultGenRandomPass(BaseConfigMemory.shortMessageRange,BaseConfigMemory.shortMessageSize);
					default  -> CommonConstant.EMPTY_VALUE;
				};
				code = String.join(CommonConstant.COMMA,type,time,code);
				code = SecretUtil.encryptAes(code,LoginConstant.VERIFY_SECRET);
				code = SecretUtil.base64EncoderString(code,StandardCharsets.UTF_8);
			}
		} catch (Exception e) {
			log.error("获取验证值发生异常，异常信息为："+e.getMessage());
		}
		return SUCCESS("login.user.verifyCodeSuccess",code);
	}
	
	/**
	 * 二维码QrConfig配置
	 */
	private final QrConfig qrConfig = new QrConfig(180,180) {
		{
			setErrorCorrection(ErrorCorrectionLevel.L);//设置纠错级别
		}
	};
	
	/**
	 * 获取二维码
	 * @see com.javaweb.web.controller.AllOpenController#getVerifyValue
	 * @param code 验证值
	 * @param request HttpServletRequest
	 * @param response HttpServletResponse
	 */
	@GetMapping("/getQrCode/{code}")
	@ControllerMethod(interfaceName="获取二维码")
	public void getQrCode(@PathVariable(value="code",required=true) String code,HttpServletRequest request,HttpServletResponse response) {
		try(ServletOutputStream out = response.getOutputStream()) {
			code = SecretUtil.base64DecoderString(code,StandardCharsets.UTF_8);
			//var qrConfig = new QrConfig(180,180);
			//qrConfig.setErrorCorrection(ErrorCorrectionLevel.L);//设置纠错级别
			QrCodeUtil.generate(code,qrConfig,"PNG",out);
		} catch(Exception e) {
			log.error("获取二维码发生异常，异常信息为："+e.getMessage());
		}
	}
	
	/**
	 * 获取图形验证码
	 * @see com.javaweb.web.controller.AllOpenController#getVerifyValue
	 * @param code 验证值
	 * @param request HttpServletRequest
	 * @param response HttpServletResponse
	 */
	@GetMapping("/getVerifyCode/{code}")
	@ControllerMethod(interfaceName="获取图形验证码")
	public void getVerifyCode(@PathVariable(value="code",required=true) String code,HttpServletRequest request,HttpServletResponse response) {
		try(ServletOutputStream out = response.getOutputStream()) {
			code = SecretUtil.base64DecoderString(code,StandardCharsets.UTF_8);
			code = SecretUtil.decryptAes(code,LoginConstant.VERIFY_SECRET);
			code = code.split(CommonConstant.COMMA)[2];
		    LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(90,20,code.length(),10);
			lineCaptcha.setGenerator(new HutoolCaptchaGenerator(code));
			lineCaptcha.createCode();
			response.setHeader("Cache-Control","no-store,no-cache");
			response.setContentType(ContentType.IMAGE_JPEG.getMimeType());//image/jpeg
			lineCaptcha.write(out);
		} catch(Exception e) {
			log.error("获取图形验证码发生异常，异常信息为："+e.getMessage());
		}
	}
	
	/* -------------------------------------------------- 分界线 -------------------------------------------------- */

	/**
	 * 用户登录（目前仅限PC端）
	 * <p>
	 * 特别注意：这里针对不同的错误输出了较为详细的校验信息，实际项目中校验信息可以稍微笼统点！！！
	 * <p>
	 * @param userLoginRequest 用户登录请求参数
	 * @return BaseResponseResult 接口通用返回参数
	 */
	@PostMapping("/pcLogin")
	@ControllerMethod(interfaceName="用户登录")
	public BaseResponseResult pcLogin(@RequestBody @Validated UserLoginRequest userLoginRequest) {
		//验证唯一值解密校验
		var code = userLoginRequest.getCode();
		try {
			code = SecretUtil.base64DecoderString(code,StandardCharsets.UTF_8);
			code = SecretUtil.decryptAes(code,LoginConstant.VERIFY_SECRET);
		}catch (Exception e) {
			throw new ServiceException("validated.user.captcha.fail");
		}
		//在验证码未失效的时间内，已被使用过的验证码不能再次被使用
		String time = code.split(CommonConstant.COMMA)[1];
		long second = DateUtil.getDuration(DateUtil.getDateTime(time,DateUtil.DATETIME_PATTERN_TYPE1),LocalDateTime.now()).getSeconds();
		long captchaExpireSecond = BaseConfigMemory.captchaExpireSecond;
		if(second<=0||second>captchaExpireSecond) {
			throw new ServiceException("validated.user.captcha.fail");
		}
		Object value = valueOperations.get(LoginConstant.CAPTCHA_KEY_PREFIX+userLoginRequest.getCode());
		if(value!=null) {
			throw new ServiceException("validated.user.captcha.fail");
		}
		this.captchaExpireReset(time,userLoginRequest);
		//验证码长度校验
		String captchaCode = userLoginRequest.getCaptchaCode();
		int limitCaptchaSize = BaseConfigMemory.captchaSize;
		if(limitCaptchaSize!=captchaCode.length()) {
			throw new ServiceException("validated.user.captcha.lengthError");
		}
		//验证码校验
		if(!(code.split(CommonConstant.COMMA)[2].equalsIgnoreCase(captchaCode))) {//这里为了方便输入忽略了大小写
			throw new ServiceException("validated.user.captcha.fail");
		}
		//密码校验
		String password = this.decodePassword(userLoginRequest.getPassword(),userLoginRequest.getTime());
		//判断密码是否符合一定的规则，如长度最少3位、必须包含数字和字母等
		if(!PatternUtil.isPattern(password,LoginConstant.PASSWORD_REGEXP_PATTERN)) {
			password = CommonConstant.EMPTY_VALUE;
		}
		userLoginRequest.setPassword(password);
		if(StringUtil.isEmpty(userLoginRequest.getPassword())) {
			throw new ServiceException("validated.user.password.pattern");
		}
		userLoginRequest.setPassword(password);
		//获取用户信息
		User user = this.getUser(time,userLoginRequest);
		//TODO
        //TokenData设置
        TokenData tokenData = this.getToken(user,userLoginRequest);
        String key = super.getRedisTokenKey(tokenData);//key值核心组成：userId,clientType,loginWay
        String repeatLoginHandle = BaseConfigMemory.repeatLoginHandle;
        if(!("1".equals(repeatLoginHandle))) {//不允许重复登录
        	if(super.getDateFromRedisByKey(key)!=null) {
        		this.captchaExpireReset(time,userLoginRequest);
        		//throw new ServiceException("login.user.userAlreadyLogin");
        	}
        }
        super.setDataToRedis(key,tokenData,Duration.ofMinutes(BaseConfigMemory.redistokenExpireMinutes));
        //个人推荐这里只是获取登录token，其它信息通过其它接口获得
		return getBaseResponseResult(HttpCodeEnum.SUCCESS,"login.user.loginSuccess",tokenData.getToken());
	}
	
	/* -------------------------------------------------- 分界线 -------------------------------------------------- */
	
	/**
	 * 重设验证码失效时间
	 * @param time 时间（形如：yyyyMMddHHmmss）
	 * @param userLoginRequest UserLoginRequest
	 */
	private void captchaExpireReset(String time,UserLoginRequest userLoginRequest) {
		long newExpireSecond = DateUtil.getDuration(LocalDateTime.parse(time,DateTimeFormatter.ofPattern(DateUtil.DATETIME_PATTERN_TYPE1)),LocalDateTime.now()).getSeconds();
		if(newExpireSecond>0) {
			valueOperations.set(LoginConstant.CAPTCHA_KEY_PREFIX+userLoginRequest.getCode(),userLoginRequest.getCode(),newExpireSecond);
		}
	}
	
	/**
	 * 解码密码
	 * <p>
	 * 前端获取用户输入的密码（如abc123）和当前时间（如20220408124351），
	 * 如果时间最后一位是奇数就是Base64加密(当前时间+密码)，如果时间最后一位是偶数就是Base64加密(密码+当前时间)，
	 * 这里得到的结果就是：MjAyMjA0MDgxMjQzNTFhYmMxMjM=
	 * <p>
	 * 当前时间（如20220408124351）将2和0对应字符串MjAyMjA0MDgxMjQzNTFhYmMxMjM=的下标交换，即A和M交换，
	 * 得到AjMyMjA0MDgxMjQzNTFhYmMxMjM=，然后是2和2交换，以此类推，最后得到的结果就是：MjjAyMA0MDgxMjQzNTFhYmMxMjM=
	 * <p>
	 * 后端解密就是倒着来，对当前时间（如20220408124351）将1和5对应字符串MjjAyMA0MDgxMjQzNTFhYmMxMjM=的下标交换，即j和M交换，
	 * 然后是4和3交换，以此类推，最后得到的结果就是：MjAyMjA0MDgxMjQzNTFhYmMxMjM=，对其Base64解密就是：20220408124351abc123，
	 * 最后根据前面说的时间最后一位，这里是1（奇数），即当前时间+密码，所以密码为：abc123
	 * @param password 待解码的密码
	 * @param time 时间（yyyyMMddHHmmss）
	 * @return String 解码后的密码
	 */
	protected String decodePassword(String password,String time) {
		String out = CommonConstant.EMPTY_VALUE;
    	try{
			char[] charArray = password.toCharArray();
			int one,another;char tmp;
			for(int i=time.length()-1;i>=0;){
				one = Integer.parseInt(String.valueOf(time.charAt(i--)));
				another = Integer.parseInt(String.valueOf(time.charAt(i--)));
				tmp = charArray[one];
				charArray[one] = charArray[another];
				charArray[another] = tmp;
			}
			out = SecretUtil.base64DecoderString(String.valueOf(charArray),StandardCharsets.UTF_8);
			if((Long.parseLong(time)&1)==0){//偶数
				out = out.substring(0,out.length()-time.length());
			}else{//奇数
				out = out.substring(time.length(),out.length());
			}
		}catch(Exception e){
			//do nothing
		}
		return out;
	}
	
	/**
	 * 加密密码
	 * @see AllOpenController#decodePassword
	 * @param password 待加密的密码
	 * @param time 时间（yyyyMMddHHmmss）
	 * @return 加密后的密码
	 */
	protected String encodePassword(String password,String time) {
    	String out = null;
    	try{
    		if((Long.parseLong(time)&1)==0){//偶数
    			out = password + time;
    		}else{//奇数
    			out = time + password;
    		}
    		out = SecretUtil.base64EncoderString(out,StandardCharsets.UTF_8);
    		char[] charArray = out.toCharArray();
    		int one,another;char tmp;
    		for(int i=0;i<time.length();){
    			one = Integer.parseInt(String.valueOf(time.charAt(i++)));
				another = Integer.parseInt(String.valueOf(time.charAt(i++)));
				tmp = charArray[one];
				charArray[one] = charArray[another];
				charArray[another] = tmp;
    		}
    		out = String.valueOf(charArray);
    	}catch(Exception e){
    		//do nothing
    	}
    	return out;
	}
	
	/**
	 * 获取用户信息
	 * <p>
	 * 默认管理员用户ID为-1，参见SystemConstant.ADMIN_USER_ID，
	 * 首先获取管理员信息，如果没有获取到则自动创建管理员信息；如果获取到管理员信息，
	 * 则比对用户输入的账号、密码是否与管理员账号、密码相等，如果相等，则返回管理员信息，
	 * 如果不相等，再去数据库查询
	 * @param userLoginRequest 用户登录请求参数
	 * @param time 时间（形如：yyyyMMddHHmmss）
	 * @return User 用户信息
	 */
	private User getUser(String time,UserLoginRequest userLoginRequest) {
		User adminUser = super.getAdminUser();
		/** 这样做的目的是始终想让数据库有一条不变的管理员信息的数据 start */
		//获取admin用户。个人推荐符合二点原则（1：根据账号密码应该只会查出唯一一条来；2：账号密码可以读取配置表获得，但是当修改配置表参数值时需要保证第1条）
		User user = userService.userDetail(AdminConstant.ADMIN_USER_ID);
		if(user==null) {
			user = adminUser;
			userService.userAdd(user);
		} else {
			ObjectOperateUtil.copyProperties(adminUser,user);
		}
		/** 这样做的目的是始终想让数据库有一条不变的管理员信息的数据 end */
		if((1==userLoginRequest.getClientType())&&(1==userLoginRequest.getLoginWay())) {
			String dbForAdminUsername = user.getUserName();
			String dbForAdminPassword = user.getPassword();
			String username = userLoginRequest.getUsername();
			String password = CommonConstant.EMPTY_VALUE;try {password = SecretUtil.getSecret(userLoginRequest.getPassword(),"SHA-256");} catch (Exception e) {}
			if((Objects.equals(username,dbForAdminUsername))&&(Objects.equals(password,dbForAdminPassword))) {//管理员
				user.setFirstLoginDate(null);
				user.setCreateDate(null);
			}else {//非管理员
				userLoginRequest.setPassword(password);
				user = userService.userLogin(userLoginRequest);
				if(user==null) {
					throw new ServiceException("login.user.userNameOrPassword");
				}
				if(!(Objects.equals(user.getPassword(),userLoginRequest.getPassword()))) {
					throw new ServiceException("login.user.userNameOrPassword");
				}
				if(user.getFirstLoginDate()==null) {
					user.setFirstLoginDate(new Date());
			    }
			}
		} else {
			throw new ServiceException("login.user.loginWayError");
		}
	    if((user.getStatus()==1)&&(!(AdminConstant.ADMIN_USER_ID.equals(user.getUserId())))) {
	    	throw new ServiceException("login.user.userLocked");
	    }
	    if(user.getPasswordExpireDate()!=null&&DateUtil.isDateEarly(DateUtil.dateToLocalDateTime(user.getPasswordExpireDate()),LocalDateTime.now())) {
	    	throw new ServiceException("login.user.passwordExpire");
	    }
        user.setLastLoginDate(new Date());
        userService.userModify(user);
		return user;
	}
	
	/**
	 * token数据封装
	 * @param user 用户信息
	 * @param userLoginRequest 用户登录请求参数
	 * @return TokenData TokenData
	 */
	private TokenData getToken(User user,UserLoginRequest userLoginRequest) {
		String platformName = StringUtil.objectEasy2String(BaseSystemMemory.currentActivePropertyMap.get("spring.application.name"));
		//如果user其它信息不想返回前端或者需要进行脱敏处理，也可以在这里处理，这里就处理了一个密码
		user.setPassword(null);
		TokenData tokenData = new TokenData();
		List<Module> moduleList = moduleService.getModule(user.getUserId(),platformName);//获取模块列表
		String date = DateUtil.getStringDate(DateUtil.DATETIME_PATTERN_TYPE1);
		String token = getTokenForEasyWay(user,userLoginRequest,date);
		tokenData.setToken(token);
		tokenData.setUser(user);
		tokenData.setClientType(userLoginRequest.getClientType());
		tokenData.setLoginWay(userLoginRequest.getLoginWay());
		tokenData.setDate(date);
		tokenData.setPageUrlList(getPageUrlList(moduleList));//获得pageUrl列表
		tokenData.setAliasList(getAliasList(moduleList));//获得alias列表
		tokenData.setApiUrlList(getApiUrlList(moduleList));//获得ApiUrl列表
		setRsaKey(tokenData);
		//获得目录、菜单列表
		List<Module> modules = moduleList.stream().filter(i->1==i.getModuleType()||2==i.getModuleType())
				                                  .sorted(Comparator.comparing(Module::getModuleType))
				                                  .sorted(Comparator.comparing(Module::getOrders)).toList();
		List<SidebarInfoResponse> menuListForTree = new ArrayList<>();
		menuListForTree = ObjectOperateUtil.copyListProperties(modules,SidebarInfoResponse.class);
		//menuListForTree = setTreeList(menuListForTree,null);//setTreeList(menuListForTree);
		tokenData.setMenuListForTree(menuListForTree);//这里没有封装成树形结构，而是由前端来封装，因为前端组件都不大一样
		if(!(AdminConstant.ADMIN_USER_ID.equals(user.getUserId()))){//非管理员时的数据权限处理
			List<ExcludeInfoResponse> excludeInfoResponseList = interfacesService.getExcludeInfoResponseList(user.getUserId());
			tokenData.setExcludeInfoResponseList(excludeInfoResponseList);
		}
		return tokenData;
	}
	
	/**
	 * 简单获得token
	 * @param user 用户信息
	 * @param userLoginRequest 用户请求参数
	 * @param date 日期（yyyyMMddHHmmss）
	 * @return String token
	 */
	private String getTokenForEasyWay(User user,UserLoginRequest userLoginRequest,String date) {
		//获取0到Integer.MAX_VALUE-1间的随机数
		String randomNumber = String.valueOf(MathUtil.getRandomNumForLCRC(Integer.MAX_VALUE-1));
		//740600474:1138f985-6b8a-4a2b-983a-abbe87056899:20211109142454
		String out = randomNumber + CommonConstant.COLON + SecretUtil.getRandomUUID(false) + CommonConstant.COLON + date;
		//740600474:1138f985-6b8a-4a2b-983a-abbe87056899:20211109142454,-1,1,1
		out = out + CommonConstant.COMMA + user.getUserId() + CommonConstant.COMMA + userLoginRequest.getClientType() + CommonConstant.COMMA + userLoginRequest.getLoginWay();
		try {
			//ymWCwezIKsJ5ScfmfU8XGTI/inVjNRO7fQhw95OqTd/3JWKEkGXHnsLdrxl+iI7Z3uONRFANPVqjVAFAip6aClNITYV/iTib2mp9en9ho3o=
			out = SecretUtil.encryptAes(out,LoginConstant.TOKEN_AES_KEY);
			//eW1XQ3dleklLc0o1U2NmbWZVOFhHVEkvaW5Wak5STzdmUWh3OTVPcVRkLzNKV0tFa0dYSG5zTGRyeGwraUk3WjN1T05SRkFOUFZxalZBRkFpcDZhQ2xOSVRZVi9pVGliMm1wOWVuOWhvM289
			out = SecretUtil.base64EncoderString(out,StandardCharsets.UTF_8);
		} catch (Exception e) {
			//do nothing
		}
		return out;
	}
	
	/**
	 * 获得pageUrl列表（供前端使用）
	 * @param moduleList 模块列表
	 * @return List<String> pageUrl列表
	 */
	private List<String> getPageUrlList(List<Module> moduleList) {
		/**
		List<String> pageUrlList = new ArrayList<>();
		for(int i=0;i<moduleList.size();i++) {
			String pageUrl = moduleList.get(i).getPageUrl();
			if(!StringUtil.isEmpty(pageUrl)) {
				pageUrlList.add(pageUrl);
			}
		}
		return pageUrlList.stream().distinct().toList();
		*/
		return moduleList.stream().filter(e->!StringUtil.isEmpty(e.getPageUrl())).map(e->e.getPageUrl()).distinct().toList();
	}
	
	/**
	 * 获得alias列表（供前端使用）
	 * @param moduleList 模块列表
	 * @return List<String> alias列表
	 */
	private List<String> getAliasList(List<Module> moduleList) {
		/**
		List<String> aliasList = new ArrayList<>();
		for(int i=0;i<moduleList.size();i++){
			Module module = moduleList.get(i);
			String alias = module.getAlias();
			if((!StringUtil.isEmpty(alias))&&(3==module.getModuleType())){
				aliasList.add(alias);
			}
		}
		return aliasList.stream().distinct().toList();
		*/
		return moduleList.stream().filter(e->(!StringUtil.isEmpty(e.getAlias()))&&(3==e.getModuleType())).map(e->e.getAlias()).distinct().toList();
	}
	
	/**
	 * 获得apiUrl列表
	 * @param moduleList 模块列表
	 * @return List<String> apiUrl列表
	 */
	private List<String> getApiUrlList(List<Module> moduleList) {
		List<String> apiUrlList = new ArrayList<>();
		for(int i=0;i<moduleList.size();i++){
			String apiUrl = moduleList.get(i).getApiUrl();
			if(!StringUtil.isEmpty(apiUrl)) {
				String apiUrls[] = apiUrl.split(CommonConstant.COMMA);//某一操作可能会调用多个附属操作（即API接口），多个附属操作约定用逗号分开
				apiUrlList.addAll(Arrays.asList(apiUrls));//for(String each:apiUrls){apiUrlList.add(each);}
			}
		}
		return apiUrlList.stream().distinct().toList();
	}
	
	/**
	 * RSA秘钥设置
	 * @param tokenData TokenData
	 */
	private void setRsaKey(TokenData tokenData) {
		RsaKey rsaKey = RsaUtil.getRsaKey();
		tokenData.setRsaPublicKey1(rsaKey.getRsaStringPublicKey());
		tokenData.setRsaPrivateKey1(rsaKey.getRsaStringPrivateKey());
		rsaKey = RsaUtil.getRsaKey();
		tokenData.setRsaPublicKey2(rsaKey.getRsaStringPublicKey());
		tokenData.setRsaPrivateKey2(rsaKey.getRsaStringPrivateKey());
	}
	
	/**
	//封装成树形结构集合（递归版）
	private List<SidebarInfoResponse> setTreeList(List<SidebarInfoResponse> originList,SidebarInfoResponse module){
		List<SidebarInfoResponse> moduleList = new ArrayList<>();
		for (int i=0;i<originList.size();i++) {
			SidebarInfoResponse currentModule = originList.get(i);
			//这里树形结构处理时需要parentId只能为null，不能为空或其它值（这个在模块新增和修改时已经控制了）
			//如果是Long类型这种封装类型一定要用equals或longValue()比较！！！，形如：module.getModuleId().longValue()==currentModule.getParentId().longValue()
			if((module!=null&&module.getModuleId().equals(currentModule.getParentId()))||(module==null&&currentModule.getParentId()==null)){
				currentModule.setList(setTreeList(originList,currentModule));
				moduleList.add(currentModule);
			}
		}
		return moduleList;
	}
	
	//封装成树形结构集合（非递归版）[代码应该没问题，但是写的不好]
	private List<SidebarInfoResponse> setTreeList(List<SidebarInfoResponse> list){
		List<List<SidebarInfoResponse>> sortList = new ArrayList<>();//有序集
		int count = 0;
		while(list.size()>0) {
			if(sortList.size()==0){//空有序集，需要初始化
				List<SidebarInfoResponse> temp = new ArrayList<>();
				Iterator<SidebarInfoResponse> it = list.iterator();
				while(it.hasNext()) {
					SidebarInfoResponse sir = it.next();
					if(sir.getParentId()==null) {
						temp.add(sir);
						it.remove();
					}
				}
				sortList.add(temp);
				count++;
			}else{//非空
				List<SidebarInfoResponse> temp = new ArrayList<>();
				List<SidebarInfoResponse> parentLevel = sortList.get(count-1);//始终获得上一层的数据
				for(int i=0;i<parentLevel.size();i++) {
					Iterator<SidebarInfoResponse> it = list.iterator();
					while(it.hasNext()) {
						SidebarInfoResponse sir = it.next();
						if(sir.getParentId().equals(parentLevel.get(i).getModuleId())) {
							temp.add(sir);
							it.remove();
						}
					}
				}
				sortList.add(temp);
				count++;
			}
		}
		int lower = sortList.size()-1;
		int higher = lower - 1;
		while(higher>=0) {
			List<SidebarInfoResponse> lowerList = sortList.get(lower);
			List<SidebarInfoResponse> higherList = sortList.get(higher);
			for(int i=0;i<lowerList.size();i++) {
				SidebarInfoResponse outer = lowerList.get(i);
				for(int j=0;j<higherList.size();j++) {
					SidebarInfoResponse inner = higherList.get(j);
					if(inner.getModuleId().equals(outer.getParentId())) {
						if(inner.getList()==null||inner.getList().size()==0) {
							inner.setList(new ArrayList<SidebarInfoResponse>());
						}
						List<SidebarInfoResponse> temp = inner.getList();
						temp.add(outer);
						inner.setList(temp);
					}
				}
			}
			lower--;
			higher--;
		}
		return sortList.get(0);
	}
    */
	
}